package carcassonne.server.ioc;

import carcassonne.lobby.Room;
import carcassonne.lobby.RoomManager;
import carcassonne.server.websocket.WebsocketContext;
import carcassonne.server.websocket.message.MessageIn;
import carcassonne.server.websocket.message.MessageOut;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.apache.log4j.Logger;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

public class WebSocketMessageDispatcher extends AbsMessageDispatcher {

    private static final Logger logger = Logger.getLogger(WebSocketMessageDispatcher.class);

    public WebSocketMessageDispatcher(List<Object> controllers) {
        super(controllers);
    }

    @Override
    protected void MappingMVC() {

        for (Object controller : Controllers) {
            Class<?> clazz = controller.getClass();

            RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
            String controllerPath = requestMapping.value();
            if (!controllerPath.startsWith("/")) controllerPath = "/" + controllerPath;

            Method[] methods = clazz.getMethods();

            for (Method method : methods) {
                if (!method.isAnnotationPresent(WebSocketMessageHandler.class)) continue;

                WebSocketMessageHandler handler = method.getAnnotation(WebSocketMessageHandler.class);
                String methodPath = handler.path();
                if (!methodPath.startsWith("/")) methodPath = "/" + methodPath;
                String requestPath = controllerPath + methodPath;
                controllerMapping.put(requestPath, controller);
                WebsocketFunctions.put(requestPath, method);
            }
        }
    }

    public void Dispatch(Channel channel, MessageIn message, String raw) {
        Object controller = controllerMapping.get(message.path);
        Method method = WebsocketFunctions.get(message.path);
        if (method == null) {
            return;
        }
        Room room = RoomManager.GetInstance().GetRoom(channel);
        if (room == null) {
            channel.writeAndFlush(new TextWebSocketFrame(MessageOut.Error("room not found: " + message.path).ToJsonString()));
            return;
        }
        try {
            method.invoke(controller, new WebsocketContext(channel, room, message, raw));
        } catch (InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
//            channel.writeAndFlush(new TextWebSocketFrame(MessageOut.Error("invoke method error: " +  message.path + ", " + e.getMessage()).ToJsonString()));
            StringBuilder sb = new StringBuilder();
            sb.append("invoke method error: ").append(message.path).append(',').append(e.getMessage()).append('\n');
            for (StackTraceElement st : e.getStackTrace()) {
                sb.append(st.toString()).append('\n');
            }
            channel.writeAndFlush(new TextWebSocketFrame(MessageOut.Error(sb.toString()).ToJsonString()));
        } catch (Exception e) {
            logger.warn("unknown error: " + e.getMessage());
        }
    }
}
